# -*- coding: utf-8 -*-
"""GitLab MR分析器"""

import requests
import re
from typing import Dict, List, Optional
from pathlib import Path
import tempfile
import subprocess
import os


class GitLabMRAnalyzer:
    """GitLab MR分析器，用于拉取和分析MR代码变更"""
    
    def __init__(self, gitlab_url: str = "https://gitlab.com", private_token: str = None):
        """
        初始化GitLab MR分析器
        
        Args:
            gitlab_url: GitLab服务器地址
            private_token: GitLab私有访问令牌
        """
        self.gitlab_url = gitlab_url.rstrip('/')
        self.private_token = private_token
        self.headers = {}
        if private_token:
            self.headers["PRIVATE-TOKEN"] = private_token
    
    def parse_mr_url(self, mr_url: str) -> Dict[str, str]:
        """
        解析MR URL，提取项目信息和MR ID
        
        Args:
            mr_url: MR URL，例如：https://gitlab.com/group/project/-/merge_requests/123
        
        Returns:
            包含project_id和mr_iid的字典
        """
        # 支持多种GitLab URL格式
        # https://gitlab.com/group/project/-/merge_requests/123
        # https://gitlab.example.com/group/subgroup/project/-/merge_requests/456
        
        pattern = r'(?:https?://)?([^/]+)/(.+?)/-/merge_requests/(\d+)'
        match = re.search(pattern, mr_url)
        
        if not match:
            raise ValueError(f"无效的MR URL格式: {mr_url}")
        
        gitlab_host = match.group(1)
        project_path = match.group(2)
        mr_iid = match.group(3)
        
        # 更新gitlab_url如果从URL中检测到不同的host
        if gitlab_host not in self.gitlab_url:
            self.gitlab_url = f"https://{gitlab_host}"
        
        # URL编码项目路径
        project_id = requests.utils.quote(project_path, safe='')
        
        return {
            "project_id": project_id,
            "mr_iid": mr_iid,
            "project_path": project_path
        }
    
    def get_mr_info(self, project_id: str, mr_iid: str) -> Dict:
        """
        获取MR基本信息
        
        Args:
            project_id: 项目ID（URL编码后的）
            mr_iid: MR内部ID
        
        Returns:
            MR信息字典
        """
        api_url = f"{self.gitlab_url}/api/v4/projects/{project_id}/merge_requests/{mr_iid}"
        
        try:
            response = requests.get(api_url, headers=self.headers, timeout=10)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f"获取MR信息失败: {str(e)}")
    
    def get_mr_changes(self, project_id: str, mr_iid: str) -> List[Dict]:
        """
        获取MR的代码变更
        
        Args:
            project_id: 项目ID
            mr_iid: MR内部ID
        
        Returns:
            变更文件列表
        """
        api_url = f"{self.gitlab_url}/api/v4/projects/{project_id}/merge_requests/{mr_iid}/changes"
        
        try:
            response = requests.get(api_url, headers=self.headers, timeout=10)
            response.raise_for_status()
            data = response.json()
            return data.get('changes', [])
        except requests.exceptions.RequestException as e:
            raise Exception(f"获取MR变更失败: {str(e)}")
    
    def get_mr_commits(self, project_id: str, mr_iid: str) -> List[Dict]:
        """
        获取MR的提交历史
        
        Args:
            project_id: 项目ID
            mr_iid: MR内部ID
        
        Returns:
            提交列表
        """
        api_url = f"{self.gitlab_url}/api/v4/projects/{project_id}/merge_requests/{mr_iid}/commits"
        
        try:
            response = requests.get(api_url, headers=self.headers, timeout=10)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f"获取MR提交历史失败: {str(e)}")
    
    def analyze_changes(self, changes: List[Dict]) -> Dict:
        """
        分析代码变更，提取关键信息
        
        Args:
            changes: 变更文件列表
        
        Returns:
            分析结果字典
        """
        analysis = {
            "total_files": len(changes),
            "added_files": [],
            "modified_files": [],
            "deleted_files": [],
            "frontend_files": [],
            "backend_files": [],
            "test_files": [],
            "config_files": [],
            "summary": {
                "total_additions": 0,
                "total_deletions": 0,
                "affected_modules": set()
            }
        }
        
        for change in changes:
            file_path = change.get('new_path') or change.get('old_path', '')
            
            # 统计增删行数
            diff = change.get('diff', '')
            additions = len([line for line in diff.split('\n') if line.startswith('+')])
            deletions = len([line for line in diff.split('\n') if line.startswith('-')])
            
            analysis["summary"]["total_additions"] += additions
            analysis["summary"]["total_deletions"] += deletions
            
            # 分类文件
            if change.get('new_file'):
                analysis["added_files"].append(file_path)
            elif change.get('deleted_file'):
                analysis["deleted_files"].append(file_path)
            else:
                analysis["modified_files"].append(file_path)
            
            # 按文件类型分类
            if self._is_frontend_file(file_path):
                analysis["frontend_files"].append(file_path)
            elif self._is_backend_file(file_path):
                analysis["backend_files"].append(file_path)
            elif self._is_test_file(file_path):
                analysis["test_files"].append(file_path)
            elif self._is_config_file(file_path):
                analysis["config_files"].append(file_path)
            
            # 提取模块信息
            module = self._extract_module_name(file_path)
            if module:
                analysis["summary"]["affected_modules"].add(module)
        
        # 转换set为list以便JSON序列化
        analysis["summary"]["affected_modules"] = list(analysis["summary"]["affected_modules"])
        
        return analysis
    
    def _is_frontend_file(self, file_path: str) -> bool:
        """判断是否为前端文件"""
        frontend_patterns = [
            r'\.(js|jsx|ts|tsx|vue|html|css|scss|sass|less)$',
            r'frontend/',
            r'client/',
            r'web/',
            r'src/components/',
            r'src/pages/',
        ]
        return any(re.search(pattern, file_path, re.IGNORECASE) for pattern in frontend_patterns)
    
    def _is_backend_file(self, file_path: str) -> bool:
        """判断是否为后端文件"""
        backend_patterns = [
            r'\.(py|java|go|rb|php|cs)$',
            r'backend/',
            r'server/',
            r'api/',
            r'service/',
            r'controller/',
            r'model/',
        ]
        return any(re.search(pattern, file_path, re.IGNORECASE) for pattern in backend_patterns)
    
    def _is_test_file(self, file_path: str) -> bool:
        """判断是否为测试文件"""
        test_patterns = [
            r'test[s]?/',
            r'__tests__/',
            r'\.(test|spec)\.(js|ts|py|java)$',
            r'_test\.py$',
        ]
        return any(re.search(pattern, file_path, re.IGNORECASE) for pattern in test_patterns)
    
    def _is_config_file(self, file_path: str) -> bool:
        """判断是否为配置文件"""
        config_patterns = [
            r'\.(json|yaml|yml|toml|ini|conf|config)$',
            r'Dockerfile',
            r'docker-compose',
            r'package\.json',
            r'requirements\.txt',
            r'pom\.xml',
        ]
        return any(re.search(pattern, file_path, re.IGNORECASE) for pattern in config_patterns)
    
    def _extract_module_name(self, file_path: str) -> Optional[str]:
        """从文件路径提取模块名称"""
        # 尝试从路径中提取有意义的模块名
        parts = file_path.split('/')
        
        # 跳过常见的顶级目录
        skip_dirs = {'src', 'lib', 'app', 'backend', 'frontend', 'client', 'server'}
        
        for part in parts:
            if part and part not in skip_dirs and not part.startswith('.'):
                return part
        
        return None
    
    def format_changes_summary(self, mr_info: Dict, analysis: Dict, changes: List[Dict]) -> str:
        """
        格式化变更摘要为Markdown
        
        Args:
            mr_info: MR基本信息
            analysis: 变更分析结果
            changes: 原始变更列表
        
        Returns:
            Markdown格式的摘要
        """
        summary = []
        summary.append(f"# MR变更分析报告\n")
        summary.append(f"## MR信息\n")
        summary.append(f"- **标题**: {mr_info.get('title', 'N/A')}")
        summary.append(f"- **作者**: {mr_info.get('author', {}).get('name', 'N/A')}")
        summary.append(f"- **源分支**: {mr_info.get('source_branch', 'N/A')}")
        summary.append(f"- **目标分支**: {mr_info.get('target_branch', 'N/A')}")
        summary.append(f"- **状态**: {mr_info.get('state', 'N/A')}")
        summary.append(f"- **创建时间**: {mr_info.get('created_at', 'N/A')}\n")
        
        summary.append(f"## 变更统计\n")
        summary.append(f"- **文件总数**: {analysis['total_files']}")
        summary.append(f"- **新增文件**: {len(analysis['added_files'])}")
        summary.append(f"- **修改文件**: {len(analysis['modified_files'])}")
        summary.append(f"- **删除文件**: {len(analysis['deleted_files'])}")
        summary.append(f"- **新增行数**: {analysis['summary']['total_additions']}")
        summary.append(f"- **删除行数**: {analysis['summary']['total_deletions']}\n")
        
        if analysis['summary']['affected_modules']:
            summary.append(f"## 影响的模块\n")
            for module in analysis['summary']['affected_modules']:
                summary.append(f"- {module}")
            summary.append("")
        
        if analysis['frontend_files']:
            summary.append(f"## 前端变更 ({len(analysis['frontend_files'])}个文件)\n")
            for file in analysis['frontend_files'][:10]:  # 只显示前10个
                summary.append(f"- {file}")
            if len(analysis['frontend_files']) > 10:
                summary.append(f"- ... 还有 {len(analysis['frontend_files']) - 10} 个文件")
            summary.append("")
        
        if analysis['backend_files']:
            summary.append(f"## 后端变更 ({len(analysis['backend_files'])}个文件)\n")
            for file in analysis['backend_files'][:10]:
                summary.append(f"- {file}")
            if len(analysis['backend_files']) > 10:
                summary.append(f"- ... 还有 {len(analysis['backend_files']) - 10} 个文件")
            summary.append("")
        
        if analysis['test_files']:
            summary.append(f"## 测试文件变更 ({len(analysis['test_files'])}个文件)\n")
            for file in analysis['test_files']:
                summary.append(f"- {file}")
            summary.append("")
        
        # 添加详细的diff信息（可选，只显示关键文件）
        key_changes = [c for c in changes if not self._is_test_file(c.get('new_path', ''))]
        if key_changes:
            summary.append(f"## 关键代码变更\n")
            for i, change in enumerate(key_changes[:5], 1):  # 只显示前5个
                file_path = change.get('new_path') or change.get('old_path', '')
                summary.append(f"### {i}. {file_path}\n")
                diff = change.get('diff', '')
                # 限制diff长度
                if len(diff) > 1000:
                    diff = diff[:1000] + "\n... (内容过长，已截断)"
                summary.append(f"```diff\n{diff}\n```\n")
        
        return '\n'.join(summary)
    
    def analyze_mr(self, mr_url: str) -> Dict:
        """
        分析MR的完整流程
        
        Args:
            mr_url: MR URL
        
        Returns:
            包含MR信息、变更分析和格式化摘要的字典
        """
        # 解析URL
        parsed = self.parse_mr_url(mr_url)
        project_id = parsed['project_id']
        mr_iid = parsed['mr_iid']
        
        # 获取MR信息
        mr_info = self.get_mr_info(project_id, mr_iid)
        
        # 获取变更
        changes = self.get_mr_changes(project_id, mr_iid)
        
        # 分析变更
        analysis = self.analyze_changes(changes)
        
        # 格式化摘要
        summary = self.format_changes_summary(mr_info, analysis, changes)
        
        return {
            "success": True,
            "mr_info": mr_info,
            "analysis": analysis,
            "changes": changes,
            "summary": summary
        }

